/**
 ******************************************************************************
 * @file           : bsp_iwdg.c
 * @brief          : IWDG Driver Implementation (STM32F4xx)
 ******************************************************************************
 * @attention
 *
 * 独立看门狗驱动实现文件
 * LSI时钟32kHz，超时范围：0.125ms ~ 32768ms
 *
 ******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/
#include "./BSP/IWDG/bsp_iwdg.h"
#include "./BSP/IWDG/bsp_iwdg_config.h"

/* Private includes ----------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define LSI_FREQ    32000   /*!< LSI时钟频率32kHz */

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static IWDG_HandleTypeDef sg_iwdg_handle;
static volatile IWDG_Error_e sg_iwdg_last_error = IWDG_OK;  /*!< IWDG最后一次错误 */
static volatile IWDG_State_e sg_iwdg_state = IWDG_STATE_IDLE;  /*!< IWDG当前状态 */

/* Private function prototypes -----------------------------------------------*/
static void iwdg_calculate_params(uint16_t timeout_ms, uint32_t *prescaler, uint32_t *reload);

/* Private functions ---------------------------------------------------------*/

/**
 * @brief       计算看门狗分频和重载值
 *
 * @param[in]   timeout_ms 超时时间(ms)
 * @param[out]  prescaler 分频系数
 * @param[out]  reload 重载值
 *
 * @retval      无
 *
 * @note        公式：Tout = (4 × 2^prescaler × reload) / 32kHz
 */
static void iwdg_calculate_params(uint16_t timeout_ms, uint32_t *prescaler, uint32_t *reload)
{
    uint32_t timeout_us = timeout_ms * 1000;
    uint32_t psc_value = 0;
    uint32_t rld_value = 0;
    
    /* 尝试不同的分频系数 */
    for (psc_value = 0; psc_value <= 6; psc_value++)  /* 0~6对应4~256分频 */
    {
        uint32_t divisor = 4 << psc_value;  /* 4, 8, 16, 32, 64, 128, 256 */
        rld_value = (timeout_us * LSI_FREQ) / (divisor * 1000000);
        
        if (rld_value <= 4095)  /* 重载值最大4095 */
        {
            break;
        }
    }
    
    /* 限制范围 */
    if (psc_value > 6)
    {
        psc_value = 6;
        rld_value = 4095;
        log_w("超时时间过长，限制为最大值");
    }
    
    if (rld_value < 1)
    {
        rld_value = 1;
        log_w("超时时间过短，限制为最小值");
    }
    
    /* 转换为HAL库定义 */
    switch (psc_value)
    {
        case 0: *prescaler = IWDG_PRESCALER_4;   break;
        case 1: *prescaler = IWDG_PRESCALER_8;   break;
        case 2: *prescaler = IWDG_PRESCALER_16;  break;
        case 3: *prescaler = IWDG_PRESCALER_32;  break;
        case 4: *prescaler = IWDG_PRESCALER_64;  break;
        case 5: *prescaler = IWDG_PRESCALER_128; break;
        case 6: *prescaler = IWDG_PRESCALER_256; break;
        default: *prescaler = IWDG_PRESCALER_32; break;
    }
    
    *reload = rld_value;
    
    /* 计算实际超时时间 */
    uint32_t divisor = 4 << psc_value;
    uint32_t actual_timeout = (divisor * rld_value * 1000) / LSI_FREQ;
    
    log_i("IWDG参数: 分频=%u, 重载=%u, 实际超时=%ums", divisor, rld_value, actual_timeout);
}

/* Exported functions --------------------------------------------------------*/

/**
 * @brief       独立看门狗初始化
 *
 * @param[in]   timeout_ms 超时时间(ms)，范围：1~32768
 *
 * @retval      无
 *
 * @note        初始化后自动启动，必须定期喂狗
 */
void iwdg_init(uint16_t timeout_ms)
{
    uint32_t prescaler = 0;
    uint32_t reload = 0;
    
    /* 计算参数 */
    iwdg_calculate_params(timeout_ms, &prescaler, &reload);
    
    /* 配置IWDG */
    sg_iwdg_handle.Instance = IWDG;
    sg_iwdg_handle.Init.Prescaler = prescaler;
    sg_iwdg_handle.Init.Reload = reload;
    
    /* 初始化并启动 */
    if (HAL_IWDG_Init(&sg_iwdg_handle) != HAL_OK)
    {
        log_e("IWDG初始化失败");
        return;
    }
    
    log_i("IWDG已启动，超时时间=%ums", timeout_ms);
}

/**
 * @brief       喂狗
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        必须在超时前调用
 */
void iwdg_feed(void)
{
    HAL_IWDG_Refresh(&sg_iwdg_handle);
    sg_iwdg_state = IWDG_STATE_RUNNING;
}

/**
 * @brief       获取IWDG最后一次错误
 *
 * @param       无
 *
 * @retval      IWDG错误码
 */
IWDG_Error_e iwdg_get_last_error(void)
{
    return sg_iwdg_last_error;
}

/**
 * @brief       打印IWDG状态（调试接口）
 *
 * @param       无
 *
 * @retval      无
 */
void iwdg_print_status(void)
{
    const char *state_str[] = {
        "IDLE",
        "RUNNING"
    };
    const char *error_str[] = {
        "IWDG_OK",
        "IWDG_ERROR_PARAM",
        "IWDG_ERROR_TIMEOUT"
    };
    
    if (sg_iwdg_state < 2)
    {
        log_i("IWDG State: %s", state_str[sg_iwdg_state]);
    }
    
    if (sg_iwdg_last_error < 3)
    {
        log_i("IWDG Last Error: %s", error_str[sg_iwdg_last_error]);
    }
}
